package com.typhoon.spring_jdbctemplate.dao.ha;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.JdbcOperations;

/**
 * 高可用DB从库HealthCheck Task
 *
 * @author Typhoon
 * @date 2017-09-17 14:04 Sunday
 * @since V2.0
 */
public class HABalanceJdbcHealthCheckTask implements Runnable {
	/**
	 * LOG
	 */
	private static Logger log = Logger.getLogger(HABalanceJdbcHealthCheckTask.class);

	/**
	 * DB从库Jdbc列表
	 */
	private List<JdbcOperations> crmB_N_JdbcList;

	/**
	 * Node健康值Bean列表
	 */
	private ArrayList<NodeHealthValueBean> healthyValueList = new ArrayList<NodeHealthValueBean>();

	/**
	 * 运行中标志
	 */
	private volatile boolean runFlag = true;

	/**
	 * 健康值 PropertyChangeSupport
	 */
	protected PropertyChangeSupport listeners = new PropertyChangeSupport(this);

	public HABalanceJdbcHealthCheckTask(List<JdbcOperations> crmB_N_JdbcList) {
		// DB从库Jdbc列表
		this.crmB_N_JdbcList = crmB_N_JdbcList;

		int len = this.crmB_N_JdbcList.size();
		NodeHealthValueBean nodeBean = null;
		for (int i = 0; i < len; i++) {
			nodeBean = new NodeHealthValueBean();
			nodeBean.setNodeIndex(i);
			// 初始状态为正常
			nodeBean.setHealthValue(0);
			// add
			this.healthyValueList.add(nodeBean);
		}
	}

	/**
	 * 添加 健康值PropertyChangeListener
	 *
	 * @param listener
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		this.listeners.addPropertyChangeListener(listener);
	}

	/**
	 * 移除 健康值PropertyChangeListener
	 *
	 * @param listener
	 */
	public void removePropertyChangeListener(PropertyChangeListener listener) {
		this.listeners.removePropertyChangeListener(listener);
	}

	/**
	 * 取得健康值
	 *
	 * @param jdbcOpers
	 * @return -1: 表示已经Down; 0-4级;
	 */
	private int getHealthyValue(JdbcOperations jdbcOpers) {
		int result = 0;
		try {
			long beforeMS = System.currentTimeMillis();
			// query 主要用来做DB存活性以及性能探测
			jdbcOpers.queryForObject("select 1", Object.class);
			long afterMS = System.currentTimeMillis();

			// diff
			long diff = afterMS - beforeMS;

			// log
			// log.warn("diff=" + diff);
			if (diff >= 0 && diff <= 500) {// 小于500毫秒为0级
				// 执行时长0-500ms内为正常
				result = 0;
			} else if (diff > 500 && diff <= 1000) {// 500毫秒到1秒之间是1级
				// 执行时长500-1000ms内为降1级
				result = 1;
			} else if (diff > 1000 && diff <= 3000) {// 1秒到3秒之间是2级
				result = 2;
			} else if (diff > 3000 && diff <= 5000) {// 3秒到5秒之间是3级
				result = 3;
			} else if (diff > 5000) {// 大于5秒是4级
				result = 4;
			}
		} catch (Exception e) {// 如果出现异常,认为DB挂掉
			result = -1;
		}

		return result;
	}

	/**
	 * 检测所有Node的健康值(5秒钟检查一下 )
	 */
	public void run() {
		while (this.runFlag) {
			try {
				int len = this.crmB_N_JdbcList.size();
				JdbcOperations jdbcOpers = null;
				NodeHealthValueBean nodeBean = null;
				int healthyValue = -1;
				for (int i = 0; i < len; i++) {
					jdbcOpers = this.crmB_N_JdbcList.get(i);
					//
					healthyValue = getHealthyValue(jdbcOpers);// 获得jdbc节点健康值
					nodeBean = this.healthyValueList.get(i);// 健康节点
					if (nodeBean.getHealthValue() == healthyValue) {// 上一次节点健康值和当前健康值一致,不需要变动
						continue;
					}
					//节点健康值有变化
					NodeHealthValueBean oldValue = new NodeHealthValueBean();//节点旧健康值
					oldValue.setNodeIndex(i);
					oldValue.setHealthValue(nodeBean.getHealthValue());

					NodeHealthValueBean newValue = new NodeHealthValueBean();//节点新健康值
					newValue.setNodeIndex(i);
					newValue.setHealthValue(healthyValue);

					PropertyChangeEvent event = new PropertyChangeEvent(this, "healthyValue", oldValue, newValue);
					// fire 健康值变化event
					this.listeners.firePropertyChange(event);//触发监听器操作

					// set new healthvalue
					nodeBean.setHealthValue(healthyValue);//设置节点的新健康值
					// set lastUpdate
					nodeBean.setLastUpdate(new Date());
				}
			} catch (Exception e) {
				log.warn(e.getMessage());
			}

			try {
				// sleep 5 秒
				Thread.sleep(5000);
			} catch (Exception e) {
				log.error("exception",e);
			}
		}
	}

}
